package org.jnarcissus;

import java.util.Arrays;

import net.sf.cglib.core.CodeEmitter;
import net.sf.cglib.core.Constants;
import net.sf.cglib.core.Signature;
import net.sf.cglib.transform.ClassEmitterTransformer;

import org.objectweb.asm.Label;
import org.objectweb.asm.Type;

public class JNarcissusClassEmitterTransformer extends ClassEmitterTransformer {

	private static final String GET_MONITORED_DATA_METHOD_NAME = "getMonitoredData";

	private static final String MONITORED_DATA_FIELD_NAME = "monitoredData";

	@Override
	public void begin_class(int version, int access, String className, Type superType, Type[] interfaces, String source) {

		// Adds some additional interfaces here because if they are supplied at
		// the Enhancer, their methods would be intercepted, and we don't want
		// that.
		Type[] additionalInterfaces = null;

		if (interfaces != null) {

			additionalInterfaces = Arrays.copyOf(interfaces, interfaces.length + 1);

			additionalInterfaces[interfaces.length] = IMonitoredInstance.TYPE;

		} else {

			additionalInterfaces = new Type[] { IMonitoredInstance.TYPE };
		}

		super.begin_class(version, access, className, superType, additionalInterfaces, source);

		declare_field(Constants.ACC_PRIVATE, MONITORED_DATA_FIELD_NAME, MonitoredInstanceData.TYPE, null);

		emitGetMonitoredInvocationsMethod();
	}

	public void emitGetMonitoredInvocationsMethod() {

		CodeEmitter e = begin_method(Constants.ACC_PUBLIC, new Signature(GET_MONITORED_DATA_METHOD_NAME, MonitoredInstanceData.TYPE,
				new Type[] {}), null);

		Label nonNullLbl = e.make_label();

		e.load_this();
		e.getfield(MONITORED_DATA_FIELD_NAME);
		e.ifnonnull(nonNullLbl);
		e.load_this();
		e.new_instance(MonitoredInstanceData.TYPE);
		e.dup();
		e.invoke_constructor(MonitoredInstanceData.TYPE);
		e.putfield(MONITORED_DATA_FIELD_NAME);
		e.mark(nonNullLbl);
		e.load_this();
		e.getfield(MONITORED_DATA_FIELD_NAME);
		e.return_value();
		e.end_method();
	}
}
